﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using Bling.Core;
using Bling.WPF;
using Bling.Util;
using Bling.Angles;

namespace Bling.Tutorial {
  public class InnerBorder : Canvas {
    internal static readonly GetProperties<InnerBorder, double> Thick =
      "Thick".NewProperties<InnerBorder, double>(4);
    internal static readonly GetProperty<InnerBorder, Color> Start = "Start".NewProperty<InnerBorder, Color>(Colors.Transparent);
    internal static readonly GetProperty<InnerBorder, Color> End = "End".NewProperty<InnerBorder, Color>(Colors.Black);
    internal InnerBorder() { }
  }
  public class InnerBorderBl : CanvasBl<InnerBorder, InnerBorderBl> {
    public ThicknessBl Thickness {
      get {
        return new ThicknessBl(
          InnerBorder.Thick[this.Provider][0],
          InnerBorder.Thick[this.Provider][1],
          InnerBorder.Thick[this.Provider][2],
          InnerBorder.Thick[this.Provider][3]);
      }
      set {
        Thickness.All.Bind = value.All;
      }
    }
    public ColorBl Start { get { return InnerBorder.Start[this.Provider]; } set { Start.Bind = value; } }
    public ColorBl End { get { return InnerBorder.End[this.Provider]; } set { End.Bind = value; } }
    public InnerBorderBl(DSL.Expr<InnerBorder> Content)
      : base(Content) { }

    public InnerBorderBl(CanvasBl Content)
      : base(Content, new InnerBorder()) {
      ZIndex = 1;
      this.Thickness = 0d;
      IsHitTestVisible = false;
      Size = Content.Size;
      LeftTop = new Double2Bl(0, 0);
      Func<Orientation, bool, LinearGradientBrushBl> MakeShade = (orient, isEnd) => {
        var rect = new RectangleBl(this) {
          Stroke = { Thickness = 0 },
          Left = new Double2Bl(!isEnd ? 0 : Content.Width - Thickness.Right, 0)[(int) orient],
          Top = new Double2Bl(0, !isEnd ? 0 : Content.Height - Thickness.Bottom)[(int) orient],

          Size = new Double2Bl(new Double2Bl(!isEnd ? Thickness.Left : Thickness.Right, Content.Width)[(int) orient],
                             new Double2Bl(Content.Height, !isEnd ? Thickness.Top : Thickness.Bottom)[(int) orient]),
        };
        var brush = new LinearGradientBrushBl() {
          RelativeTransform = { Rotate = (90 * (new Double2Bl(isEnd ? -1 : +1, isEnd ? +1 : -1)[(int) orient])).Degrees() },
        }.Stop(this.Start, 0).Stop(this.End, 1);
        rect.Fill = brush;
        return brush;
      };
      MakeShade(Orientation.Horizontal, false);
      MakeShade(Orientation.Horizontal, true);
      MakeShade(Orientation.Vertical, false);
      MakeShade(Orientation.Vertical, true);
    }
  }

  public class TableInfo {
    public IntBl RowCount = 1000;
    public IntBl ColumnCount = 1000;
    public Func<int, int, ObjectBl> CellF = (i, j) => "D" + i + "_" + j;
    public Func<int, ObjectBl> RowHeaderF = (i) => "R" + i;
    public Func<int, ObjectBl> ColumnHeaderF = (i) => "C" + i;
    public Double2Bl CellSize = new Double2Bl(100, 50);
  }


  public class Tables : Canvas {
    private class RowColumn {
      public readonly ScrollBarBl Scroll;
      public readonly CanvasBl Header;
      public Orientation Orientation {
        get { return Scroll.Orientation.CurrentValue; }
      }
      public readonly DoubleBl Jiggy;
      public readonly IntBl ZeroIndex;
      public readonly DynamicArray<CanvasBl> Items;
      public IntBl VisibleCount;

      public void Init(TableInfo info, IntBl Count, Double2Bl AvailableSize, RowColumn Other) {
        VisibleCount = (IntBl) (AvailableSize[(int) Orientation] / info.CellSize[(int) Orientation]).Floor;
        VisibleCount = (VisibleCount > Count).Condition(Count, VisibleCount);
        Items.Count = VisibleCount + 1;
        Header.Size[(int) Orientation] = VisibleCount * info.CellSize[(int) Orientation];

        Scroll.Left = new Double2Bl(Header.Left, Other.Header.Right)[(int) Orientation];
        Scroll.Top = new Double2Bl(Other.Header.Bottom, Header.Top)[(int) Orientation];
        Scroll.Maximum = info.CellSize[(int) Orientation] * (Count - VisibleCount);
      }
      public RowColumn(TableInfo info, CanvasBl MainCanvas, Orientation orientation, Func<IntBl,ObjectBl> HeaderF) {
        this.Header = new CanvasBl(MainCanvas) {
          ClipToBounds = true,
          Left = new Double2Bl(info.CellSize.X, 0)[(int) orientation],
          Top  = new Double2Bl(0, info.CellSize.Y)[(int) orientation],
          //Background = (orientation == Orientation.Horizontal) ? Brushes.LightBlue : Brushes.Pink,
        };
        this.Header.Size[(int) orientation.Reverse()] = info.CellSize[(int) orientation.Reverse()];

        this.Scroll = new ScrollBarBl(MainCanvas) {
          Orientation = orientation,
          Minimum = 0, Value = 0,
          Change = { Small = info.CellSize[(int) orientation] },
        };
        this.ZeroIndex = (IntBl)(Scroll.Value / info.CellSize[(int) orientation]).Floor;
        this.Jiggy = (Scroll.Value % info.CellSize[(int) orientation]);
        this.Scroll.Size[(int) orientation] = Header.Size[(int) orientation];
        this.Scroll.ViewportSize = Header.Size[(int) Orientation];

        Header.LeftTop[(int) orientation.Reverse()] = 0;
        Items = new DynamicArray<CanvasBl>((idx) => {
          var item = new CanvasBl(Header) {
            Size = info.CellSize,
          };
          item.Size = info.CellSize;
          item.LeftTop[(int) orientation] = idx * info.CellSize[(int) orientation] - Jiggy;
          item.LeftTop[(int) orientation.Reverse()] = 0d;
          var lbl = new LabelBl(item) {
            LeftTop = new Double2Bl(0, 0),
            Content = HeaderF(ZeroIndex + idx),
          };
          //ScaleTo(lbl, item.Size);
          return item;
        }, (idx, item) => {
          item.Remove();
        });
      }
    }
    private RowColumn Columns;
    private RowColumn Rows;
    private DynamicArray<CanvasBl> Cells;
    public void MakeTable(TableInfo info, Double2Bl Size) {
      CanvasBl MainCanvas = this;
      this.Columns = new RowColumn(info, MainCanvas, Orientation.Horizontal, i => i.Map<object,ObjectBl>(j => info.ColumnHeaderF(j)));
      this.   Rows = new RowColumn(info, MainCanvas, Orientation.Vertical  , i => i.Map<object,ObjectBl>(j => info.   RowHeaderF(j)));
      {
        var AvailableSize = Size - info.CellSize - new Double2Bl(Rows.Scroll.Width, Columns.Scroll.Height);
        Columns.Init(info, info.ColumnCount, AvailableSize,    Rows);
        Rows   .Init(info, info.   RowCount, AvailableSize, Columns);
      }
      CanvasBl Content = new CanvasBl(MainCanvas) {
        LeftTop = info.CellSize,
        Size = new Double2Bl(Columns.Header.Width, Rows.Header.Height),
        ClipToBounds = true,
      };
      //Content.MakeInnerBorder(Colors.Transparent, Colors.Black, new PointBl(15, 15));
      new InnerBorderBl(Content) {
        Thickness = 15d,

      };

      this.Cells = new DynamicArray<CanvasBl>((idx) => {
        var row = idx / Columns.Items.Count;
        var column = idx % Columns.Items.Count;
        var item = new CanvasBl(Content) {
          Size = info.CellSize,
          LeftTop = new Double2Bl(column * info.CellSize.X - Columns.Jiggy, 
                                row    * info.CellSize.Y - Rows   .Jiggy),
        };
        var border = new RectangleBl(item) {
          Stroke = { Thickness = .5, Brush = Brushes.Black }, LeftTop = new Double2Bl(0,0), Size = info.CellSize,
        };

        var lbl = new LabelBl(item) {
          LeftTop = new Double2Bl(0,0),
          Content = (column + Columns.ZeroIndex).Combine<int, IntBl>(row + Rows.ZeroIndex).Map<object, ObjectBl>( 
            (column0, row0) => info.CellF(column0, row0)),
        };
        //ScaleTo(lbl, info.CellSize);
        return item;
      }, (idx, item) => {
        item.PanelParent = CanvasBl.None;
      }) { 
        Count = Rows.Items.Count * Columns.Items.Count,
      };
    }
    private static void ScaleTo(FrameworkElementBl e, Double2Bl sz) {
      var q = sz / e.Size;
      e.RenderTransform[new Double2Bl(0, 0)].Scale = q;
    }


    // trap the reference. 
    private DynamicArray<LabelBl> Labels;
    public void MakeCarousel(IntBl Count, Func<int,ObjectBl> F) {
      CanvasBl MainCanvas = this;
      var CellSize = new Double2Bl(100, 100);

      CanvasBl Holder = new CanvasBl(MainCanvas) {
        Width = ((MainCanvas.Width - 2d * CellSize.X) / CellSize.X).Floor * CellSize.X, Height = CellSize.Y,
        LeftTop = new Double2Bl(CellSize.X,0),
        Background = Brushes.White,
        ClipToBounds = true,
        Name = "Holder",
      };
      SliderBl Slider = new SliderBl(MainCanvas) {
        LeftTop = Holder.LeftBottom, Width = Holder.Width,
        Minimum = 0, Maximum = Count * CellSize.X - Holder.Width,
      };

      Func<IntBl, ObjectBl> G = i => i.Map<object,ObjectBl>(j => F(j), null);
      DoubleBl Offset = Slider.Value;
      IntBl IndexOffset = (IntBl)((Offset / CellSize.X).Floor);
      DoubleBl Jiggy = ((Offset / CellSize.X) % 1d);
      Labels = new DynamicArray<LabelBl>((idx) => {
        var lbl = new LabelBl(Holder) {
          Top = 0,
          Left = CellSize.X * (idx - Jiggy),
          Content = G(idx + IndexOffset),
          Font = { Weight = FontWeights.SemiBold },
          //Background = (idx == Labels.Count - 1).Condition(Brushes.Red, Brushes.Transparent)
        };
        //ScaleTo(lbl, new PointBl(100, 100));
        return lbl;
      }, (idx, lbl) => {
        lbl.Remove();
      });
      Labels.Count = ((IntBl)(Holder.Width / CellSize.X).Floor) + 1;

      CanvasBl LeftEdge = new CanvasBl(MainCanvas) {
        RightTop = Holder.LeftTop, Size = CellSize,
        ClipToBounds = true,
        Opacity = (1 - Jiggy),
      };
      //LeftEdge.RenderTransform[new Double2Bl(LeftEdge.Width, 0)].Skew.Y = (-90 * Jiggy).ToDegrees();

      new RectangleBl(LeftEdge) {
        //Fill = Brushes.Red,
        Stroke = { Brush = Brushes.White, Thickness = 1, },
        LeftTop = new Double2Bl(0,0),
        Size = LeftEdge.Size * new Double2Bl(2,1),
      };

      LabelBl LeftEdgeLabel = new LabelBl(LeftEdge) {
        Top = 0, ZIndex = 2,
        Left = LeftEdge.Width - (Jiggy) * LeftEdge.Width,
        Content = G(IndexOffset + 0),
        Foreground = Brushes.Black,
        //Background = Brushes.Green,
        Font = { Weight = FontWeights.SemiBold },
      };
      //ScaleTo(LeftEdgeLabel, CellSize);
      //LeftEdge.RenderTransform.SkewAt(LeftEdge.RightTop).Y = (-45).ToDegrees() * Jiggy;

    }
    public Tables() {
      CanvasBl MainCanvas = this;
      var hslider = new SliderBl(MainCanvas) {
        LeftBottom = new Double2Bl(0, MainCanvas.Height), ZIndex = 1,
        Width = MainCanvas.Width, Minimum = 0, Maximum = 10000, Value = 100
      };
      var vslider = new SliderBl(MainCanvas) {
        LeftBottom = hslider.LeftTop, ZIndex = 1,
        Width = MainCanvas.Width, Minimum = 0, Maximum = 10000, Value = 100
      };
      MakeTable(new TableInfo() {
        RowCount = (IntBl)hslider.Value.Round,
        ColumnCount = (IntBl)vslider.Value.Round,
      }, MainCanvas.Size - new Double2Bl(0, hslider.Height + vslider.Height));
    }
  }

}
